import {nextTick, onActivated, onBeforeMount, onMounted, onUnmounted, ref, watch} from "vue"
import { ElNotification as Notification } from 'element-plus'
import {downloadFile} from "@/utils";
import { addResizeListener, removeResizeListener } from 'element-plus/lib/utils/dom/resize-event'

/**
 * crud 组合API
 * @param props {
 *   pageSize: 列表页每页的数量
 *   title: 当前业务的标题
 *   defaultForm: 表单默认数据
 *   methods: {
 *     list(params): 列表
 *     get(id): 查询
 *     save(data): 添加
 *     update(data): 更新
 *     remove(ids): 删除
 *     download(params): 导出下载
 *   },
 *   idField: ID字段名,
 *   message: { // 提示语
 *     saved: '添加成功!',
 *     updated: '更新成功!',
 *     removed: '删除成功!'
 *   }
 *   optShow: { // 主页显示的按钮
 *       create: 添加
 *       edit: 编辑
 *       remove: 删除
 *       download: 导出
 *       reset: 重置
 *    },
 *    hook: { // 钩子函数
 *      // 搜索之前
 *      beforeSearch: ()=>{ return true },
 *      // 搜索之后
 *      afterSearch: () => { return true },
 *      // 添加之前
 *      handleCreate: () => { return true },
 *      // 编辑之前
 *      handleEdit: (data) => { return true }
 *  }
 * }
 */

/**
 * 用于标识是否是crud组件
 * @type {boolean}
 */
const IS_ESJ_CRUD = true
/**
 * 操作类型：0:关闭，1:添加 2：编辑
 * @type {{CREATE: number, EDIT: number, DEFAULT: number}}
 */
const DIALOG_TYPE = {
  DEFAULT: 0,
  CREATE: 1,
  EDIT: 2
}
/**
 * 操作状态：0：默认， 1：准备数据， 2：数据提交中
 * @type {{PROCESSING: number, PREPARED: number, NORMAL: number}}
 */
const DIALOG_STATUS = {
  NORMAL: 0,
  PREPARED: 1,
  PROCESSING: 2
}

export const useCrud = (props) => {
  // 初始化数据
  if (!props.defaultForm) {
    props.defaultForm = {}
  }
  /**
   * 增删改查api
   * @type {{list: methods.list, get: methods.get, save: methods.save, update: methods.update, remove: methods.remove}}
   */
  let methods = {
    list: (params) => { console.log('list:' + params) },
    save: (data) => { console.log('save: ' + data) },
    remove: (ids) => { console.log('remove: ' + ids) },
    update: (data) => { console.log('update: ' + data) },
    get: (id) => { console.log('get: ' + id) },
    download: (params) => { console.log('download: ' + params)}
  }
  if (props.methods) {
    for (let k in props.methods) {
      methods[k] = props.methods[k]
    }
  }

  /**
   * 钩子方法
   * @type {{beforeSearch: (function(): boolean)}}
   */
  let hook = {
    // 搜索之前
    beforeSearch: ()=>{ return true },
    // 搜索之后
    afterSearch: () => { return true },
    // 添加之前
    handleCreate: () => { return true },
    // 编辑之前
    handleEdit: (data) => { console.log(data); return true },
    // 重置查询条件前
    beforeResetQuery: () => { return true },
    // 重置查询条件后
    afterResetQuery: () => { return true }
  }
  if (props.hook) {
    for (let k in props.hook) {
      hook[k] = props.hook[k]
    }
  }

  /**
   * 操作成功提示语
   * @type {{removed: string, saved: string, updated: string}}
   */
  let message = {
    saved: '添加成功!',
    updated: '更新成功!',
    removed: '删除成功!'
  }
  if (props.message) {
    Object.assign(message, props.message)
  }
  // 主页操作栏显示哪些按钮
  let optShow = {
    create: true,
    edit: true,
    remove: true,
    download: true,
    reset: true
  }
  if (props.optShow) {
    Object.assign(optShow, props.optShow)
  }

  /**
   * 自适应底部留空距离,false/0为关闭
   * @type {Ref<UnwrapRef<number>>}
   */
  const bottomOffset = ref(90)
  if (typeof(props.bottomOffset) != 'undefined') {
    bottomOffset.value = props.bottomOffset
  }

  /**
   * 搜索栏显示开关
   * @type {Ref<UnwrapRef<boolean>>}
   */
  const searchToggle = ref(true)

  /**
   * 添加/编辑对话框属性
   * @type {ToRef<{visible: boolean, status: number, type: number, readonly title: (string|*)}>}
   */
  let dialog = ref({
    type: DIALOG_TYPE.DEFAULT,
    visible: false,
    status: DIALOG_STATUS.NORMAL,
    get title() {
      if (this.type === DIALOG_TYPE.CREATE) {
        return "添加" + props.title
      }
      if (this.type === DIALOG_TYPE.EDIT) {
        return "编辑" + props.title
      }
      return props.title
    }
  })

  /**
   * 表单数据
   * @type {{}}
   */
  let form = ref(props.defaultForm)

  /**
   * 表单引用
   * @type {ToRef<null>}
   */
  const formRef = ref(null)

  /**
   * 表格引用
   * @type {ToRef<null>}
   */
  const table = ref(null)

  /**
   * 表格数据
   * @type {ToRef<*[]>}
   */
  let tableData = ref([])

  /**
   * 查询条件
   * @type {ToRef<{}>}
   */
  let query = ref({})

  /**
   * 分页参数
   * @type {ToRef<{total: number, size: number, page: number}>}
   */
  let page = ref({
    page: 1,
    size: 20,
    total: 0
  })
  if (props.pageSize) {
    page.value.size = props.pageSize
  }

  /**
   * ID字段名
   * @type {string|*}
   */
  const idField = props.idField ? props.idField : 'id'
  /**
   * 获取数据中ID字段的值
   * @param data
   * @returns {*}
   */
  const getDataId = (data) => {
    return data[idField]
  }

  /**
   * 默认查询参数，重置的时候用
   * @type {null}
   */
  let defaultQuery = null

  /**
   * 表格数据loading状态
   * @type {Ref<UnwrapRef<boolean>>}
   */
  let tableLoading = ref(false)
  /**
   * 获取数据列表
   */
  const fetchList = async () => {
    tableLoading.value = true
    const data = await methods.list(getQueryParams()).catch(()=>{return false})
    if (data) {
      tableData.value = data.rows
      page.value.total = data.total
    }
    tableLoading.value = false
  }
  /**
   * 获取查询参数
   */
  const getQueryParams = () => {
    // 清除参数无值的情况
    Object.keys(query).length !== 0 && Object.keys(query).forEach(item => {
      if (query[item] === null || query[item] === '') query[item] = undefined
    })
    // Object.keys(params).length !== 0 && Object.keys(params).forEach(item => {
    //     if (params[item] === null || params[item] === '') params[item] = undefined
    // })
    const params = {
      page: page.value.page,
      size: page.value.size,
      ...query.value,
      ...params
    }
    // if (typeof sort !== 'undefined' && sort !== null && sort.length > 0) {
    //     params.sort = sort
    // }
    return params
  }

  /**
   * 添加
   */
  const handleCreate = () => {
    if (false === hook.handleCreate()) return
    dialog.value.type = DIALOG_TYPE.CREATE
    dialog.value.status = DIALOG_STATUS.NORMAL
    form.value = Object.assign({}, props.defaultForm)
    formRef.value && formRef.value.clearValidate()
    dialog.value.visible = true
  }
  /**
   * 编辑
   * @param data
   */
  const handleEdit = (data) => {
    if (false === hook.handleEdit(data)) return
    dialog.value.type = DIALOG_TYPE.EDIT
    dialog.value.status = DIALOG_STATUS.NORMAL
    form.value = Object.assign({}, data)
    formRef.value && formRef.value.clearValidate()
    dialog.value.visible = true
  }

  /**
   * 提交表单
   */
  const submitCE = () => {
    formRef.value.validate(async valid => {
      if (valid) {
        dialog.value.status = DIALOG_STATUS.PROCESSING
        if (dialog.value.type === DIALOG_TYPE.CREATE) {
          const result = await methods.save(form.value).catch(() => { return false })
          dialog.value.status = DIALOG_STATUS.NORMAL
          if (!result) return
          Notification.success({
            message: message.saved,
            duration: 2500
          })
          fetchList()
        } else if (dialog.value.type === DIALOG_TYPE.EDIT) {
          const result = await methods.update(form.value).catch(() => { return false })
          dialog.value.status = DIALOG_STATUS.NORMAL
          if (!result) return
          Notification.success({
            message: message.updated,
            duration: 2500
          })
          fetchList()
        } else {
          Notification.error('数据异常，请刷新重试')
        }
      }
    })
  }

  /**
   * 批量删除按钮loading状态
   * @type {Ref<UnwrapRef<boolean>>}
   */
  let batchRemoveLoading = ref(false)

  const handleRemove = async (data) => {
    const ids = []
    let isBatch = false
    if (data instanceof Array) {
      isBatch = true
      batchRemoveLoading.value = true
      data.forEach(value => ids.push(getDataId(value)))
    } else {
      ids.push(getDataId(data))
    }
    const result = await methods.remove(ids).catch(() => { return false })
    if (isBatch) {
      batchRemoveLoading.value = false
    }
    if (!result) return
    Notification.success({
      message: message.removed,
      duration: 2500
    })
    fetchList()
  }
  const handleSearch = () => {
    if (false === hook.beforeSearch()) return
    fetchList()
    hook.afterSearch()
  }
  const resetQuery = (toQuery = true) => {

    if (false === hook.beforeResetQuery()) return
    Object.keys(query.value).forEach(key => {
      query.value[key] = defaultQuery[key]
    })
    if (false === hook.afterResetQuery()) return
    // 重置参数
    if (toQuery) {
      page.value.page = 1
      fetchList()
    }
  }

  /**
   * 导出按钮loading状态
   * @type {Ref<UnwrapRef<boolean>>}
   */
  let downloadLoading = ref(false)
  const handleExport = () => {
    downloadLoading.value = true
    methods.download(getQueryParams()).then(result => {
      downloadFile(result, props.title + '数据', 'xlsx')
      downloadLoading.value = false
    }).catch(() => {
      downloadLoading.value = false
    })
  }

  /**
   * 表格选中项
   * @type {ToRef<*[]>}
   */
  let selections = ref([])
  /**
   * 选择改变
   * @param val
   */
  const selectionChangeHandler = (val) => {
    selections.value = val
  }

  /**
   * 修改当前页数
   */
  const changeCurrentPage = (currentPage) => {
    page.value.page = currentPage
    fetchList()
  }

  /**
   * 修改每页数量
   */
  const changePageSize = (pageSize) => {
    page.value.size = pageSize
    fetchList()
  }

  /**
   * 自适应表格高度
   */
  const adaptiveTableSize = () => {
    try {
      const height = window.innerHeight - table.value.$el.getBoundingClientRect().top - bottomOffset.value
      table.value.layout.setHeight(height)
      table.value.doLayout()
    } catch {}

  }
  /**
   * 表格滚动条当前位置
   */
  let lastScrollTop = 0

  /**
   * 监听表格滚动条位置
   */
  const bindTableScrollListener = () => {
    let tableWrapper = table.value.$el.getElementsByClassName('el-scrollbar__wrap') && table.value.$el.getElementsByClassName('el-scrollbar__wrap')[0]
    tableWrapper.addEventListener('scroll', function() {
      lastScrollTop = tableWrapper.scrollTop
    })
  }
  /**
   * 恢复表格滚动条位置
   */
  const reScrollTable = () => {
    try {
      let tableWrapper = table.value.$el.getElementsByClassName('el-scrollbar__wrap') && table.value.$el.getElementsByClassName('el-scrollbar__wrap')[0]
      if (tableWrapper && tableWrapper.scrollTop != lastScrollTop) {
        tableWrapper.scrollTop = lastScrollTop
      }
    } catch {}

  }

  onMounted(() => {
    defaultQuery = JSON.parse(JSON.stringify(query.value))
    fetchList()

    if (bottomOffset.value) {
      // 监听表格滚动条位置
      try {
        bindTableScrollListener()
      } catch (e) { console.log(e) }
    }


  })

  onBeforeMount(() => {
    if (bottomOffset.value) {
      addResizeListener(window.document.body, adaptiveTableSize)
    }
  })
  onUnmounted(() => {
    if (bottomOffset.value) {
      removeResizeListener(window.document.body, adaptiveTableSize)
    }
  })

  onActivated(() => {
    if (bottomOffset.value) {
      nextTick(() => {
        adaptiveTableSize()
        nextTick(() => {
          // 恢复表格滚动条位置
          reScrollTable()
        })
      })
    }

  })

  if (bottomOffset.value) {
    watch(searchToggle, () => {
      nextTick(() => {
        adaptiveTableSize()
      })
    })
  }

  return {
    IS_ESJ_CRUD,
    DIALOG_STATUS,
    table,
    tableData,
    bottomOffset,
    searchToggle,
    dialog,
    form,
    formRef,
    query,
    page,
    idField,
    optShow,

    tableLoading,
    fetchList,
    handleCreate,
    handleEdit,
    submitCE,
    batchRemoveLoading,
    handleRemove,
    handleSearch,
    resetQuery,
    selections,
    selectionChangeHandler,
    downloadLoading,
    handleExport,


    changeCurrentPage,
    changePageSize

  }
}

export default useCrud
